package com.scs.application.core.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.enums.SqlMethod;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.scs.application.consts.GlobalConsts;
import com.scs.application.core.dto.ExcelImportField;
import com.scs.application.core.entity.BaseEntity;
import com.scs.application.core.entity.FlowEntity;
import com.scs.application.core.exception.BusinessException;
import com.scs.application.core.exception.ValidException;
import com.scs.application.core.export.entity.ExportParams;
import com.scs.application.core.export.service.ExportService;
import com.scs.application.core.flow.FlowContext;
import com.scs.application.core.flow.FlowContextHolder;
import com.scs.application.core.mapper.IBaseMapper;
import com.scs.application.core.query.QueryContext;
import com.scs.application.core.utils.*;
import com.scs.application.modules.sys.adapter.FlowAdapter;
import com.scs.application.modules.sys.adapter.SerialRuleAdapter;
import com.scs.application.modules.sys.entity.Flow;
import com.scs.application.modules.sys.entity.FlowButton;
import com.scs.application.modules.sys.entity.FlowExecution;
import com.scs.application.modules.sys.entity.FlowLog;
import com.scs.application.modules.sys.service.FileService;
import com.scs.application.modules.sys.service.FlowExecutionService;
import com.scs.application.modules.sys.service.FlowLogService;
import com.scs.application.modules.upgrade.db.annotation.EntityFieldAnnotation;
import com.scs.application.modules.wm.entity.OrderBarcode;
import com.scs.application.modules.wm.entity.Stock;
import com.scs.application.modules.wm.entity.StockHistory;
import com.scs.application.modules.wm.entity.StockZero;
import com.scs.application.modules.wm.service.OrderBarcodeService;
import com.scs.application.modules.wm.service.StockHistoryService;
import com.scs.application.modules.wm.service.StockService;
import com.scs.application.modules.wm.service.StockZeroService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.Workbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import javax.validation.Validator;
import java.beans.PropertyDescriptor;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
public class BaseServiceImpl<M extends IBaseMapper<T>, T extends BaseEntity> extends ServiceImpl<M, T> implements IBaseService<T> {
    protected final Logger logger = LoggerFactory.getLogger(this.getClass());
//
//    @Autowired
//    protected ForkJoinPool forkJoinPool;

    @Autowired
    protected Validator validator;

    @Autowired
    private SqlSessionFactory sqlSessionFactory;

    @Autowired(required = false)
    @Lazy
    private FlowLogService flowLogService;

    @Autowired
    @Lazy
    protected SerialRuleAdapter serialRuleAdapter;

    @Autowired
    @Lazy
    private ExportService exportService;
    @Value("${res.real-path}")
    private String resPath;
    @Autowired
    @Lazy
    FileService fileService;

    @Autowired
    protected JdbcTemplate jdbcTemplate;

    @Autowired
    @Lazy
    private OrderBarcodeService orderBarcodeService;
    @Autowired
    @Lazy
    private StockService stockService;

    @Autowired
    @Lazy
    private StockHistoryService stockHistoryService;
    @Autowired
    @Lazy
    private StockZeroService stockZeroService;

    @Autowired
    protected DataSourceTransactionManager dataSourceTransactionManager;

    @Autowired
    protected TransactionDefinition transactionDefinition;

    @Autowired
    @Lazy
    protected FlowAdapter flowAdapter;

    @Autowired
    @Lazy
    private FlowExecutionService flowExecutionService;

    @Override
    public Page<?> page(QueryContext context) {
        String sqlStatement = SqlHelper.table(currentModelClass()).getSqlStatement("page");
        if (sqlSessionFactory.getConfiguration().hasStatement(sqlStatement)) {
            if (context.getWrapper() == null || context.getWrapper().getExpression() == null || context.getWrapper().getExpression().getNormal() == null
                    || context.getWrapper().getExpression().getNormal().size() < 1
            ) {
                HashMap<String, Object> mapParam = new HashMap<String, Object>();
                mapParam.putAll(context.getParams());
                mapParam.put("noWhere", 1);
                context.setParams(mapParam);
            }
            // 说明在mapper.xml中实现了自定义page方法
            return this.baseMapper.page(context.getPage(), context.getWrapper(), context.getParams());
        } else {
            return (Page<?>) this.page(context.getPage(), context.getWrapper());
        }
    }

    @Override
    public List<?> list(QueryContext context) {
        String sqlStatement = SqlHelper.table(currentModelClass()).getSqlStatement("list");
        if (sqlSessionFactory.getConfiguration().hasStatement(sqlStatement)) {
            // 说明在mapper.xml中实现了自定义list方法
            return this.baseMapper.list(context.getWrapper(), context.getParams());
        } else {
            return this.list(context.getWrapper());
        }
    }

    @Override
    public String exportFullData(QueryContext context, ExportParams exportParams, Map<String, Object> userParam) throws Exception {
        List result;
        if (exportParams != null && exportParams.getListEntry().endsWith("/list")) {
            result = this.list(context);
        } else if (exportParams != null && exportParams.getListEntry().endsWith("/page")) {
            result = this.page(context).getRecords();
        } else { //其他自己的实现
            String mapMethodName = exportParams.getListEntry().substring(exportParams.getListEntry().lastIndexOf("/") + 1);
            Object oResult = SpringUtils.invokeMethodIgnoreParams(this.getClass(), mapMethodName, context, userParam);
            result = ((Page<?>) oResult).getRecords();
        }

        try {
            String downloadPath = exportService.export(result, exportParams);
            return downloadPath;
        } catch (Exception e) {
            log.error("BaseServiceImpl.exportFullData", e);
            throw new BusinessException("文件生成失败，原因：" + e != null ? e.getMessage() : "未知异常");
        }
    }

    /**
     * 去掉前后空格
     *
     * @param entity
     * @return
     */
    @Override
    public boolean save(T entity) {
        try {
            return super.save(trimEntity(entity));
        }catch (Exception e) {
            log.error("save.error", e);
            log.error("save.entity");
            log.error(JSON.toJSONString(entity));
            throw new BusinessException("保存失败,原因:"+e.getMessage());
        }
    }
    /**
     * 去掉前后空格
     *
     * @param entity
     * @return
     */
    @Override
    public boolean saveOrUpdate(T entity) {
        try {
            return super.saveOrUpdate(trimEntity(entity));
        }catch (Exception e) {
            log.error("saveOrUpdate.error", e);
            log.error("saveOrUpdate.entity");
            log.error(JSON.toJSONString(entity));
            throw new BusinessException("保存失败,原因:"+e.getMessage());
        }
    }

    /**
     * 去掉前后空格
     *
     * @param entity
     * @return
     */
    @Override
    public boolean updateById(T entity) {
        return super.updateById(trimEntity(entity));
    }

    @Override
    public <R> boolean saveBatch(Class<R> modelClass, Collection<R> entityList, int batchSize) {
        String sqlStatement = SqlHelper.table(modelClass).getSqlStatement(SqlMethod.INSERT_ONE.getMethod());

        try (SqlSession batchSqlSession = sqlSessionBatch()) {
            int i = 0;
            for (R anEntityList : entityList) {
                batchSqlSession.insert(sqlStatement, anEntityList);
                if (i >= 1 && i % batchSize == 0) {
                    batchSqlSession.flushStatements();
                }
                i++;
            }
            batchSqlSession.flushStatements();
        }
        return true;
    }

    @Override
    public void handleFlowChange() {
        FlowContext flowContext = FlowContextHolder.currentContext();

        T entity = this.getById(flowContext.getBusId());
        flowContext.setBusEntity(entity);
        if (entity == null) {
            BusinessException.throwValidFail("流程处理失败：数据不存在");
        }

        if (entity.getClass().isAssignableFrom(FlowEntity.class)) {
            BusinessException.throwValidFail("流程处理失败：父类不为流程实体");
        }

        TransactionStatus transactionStatusFlowExecution = null;
        TransactionStatus transactionStatusMain = null;
        try {
            //流程状态
            FlowEntity flowEntity = (FlowEntity) entity;
            if (!flowEntity.getFlowStatus().equals(flowContext.getButton().getValueIf())) {
                BusinessException.throwValidFail("流程校验失败：当前数据状态【%s】，按钮所需状态【%s】，请尝试刷新后重试",flowEntity.getFlowStatus(),flowContext.getButton().getValueIf());
            }

            //流程实例状态
            transactionStatusFlowExecution = dataSourceTransactionManager.getTransaction(transactionDefinition);
            FlowExecution flowExecution = flowExecutionService.getByBusIdWithCreate(flowEntity, flowContext);
            if (!flowExecution.getFlowStatus().equals(flowContext.getButton().getValueIf())) {
                if (flowContext.getFlow().getFlagCheckExecution() != null && flowContext.getFlow().getFlagCheckExecution()) {
                    BusinessException.throwValidFail("流程校验失败：当前流程实例状态【%s】，按钮所需状态【%s】，请尝试刷新后重试",flowExecution.getFlowStatus(),flowContext.getButton().getValueIf());
                }
                log.error("流程实例与数据状态出现了不匹配，流程编码={}，实例主键={}",flowContext.getFlow().getCode(),flowExecution.getId());
            }
            //更新流程示例状态
            LocalDateTime localDateTime = LocalDateTime.now();
            flowExecution.setFlowStatus(flowContext.getButton().getValueTo()).setFlowStatusText(flowContext.getFlowStatusTextTo());
            flowExecution.setBusUser(UserUtils.currentUser().getName()).setBusDate(DateUtils.now());
            flowExecutionService.updateById(flowExecution);
            dataSourceTransactionManager.commit(transactionStatusFlowExecution);


            transactionStatusMain = dataSourceTransactionManager.getTransaction(transactionDefinition);
            beforeHandleFlowChange(flowContext, (T) flowEntity);

            flowEntity.setFlowStatus(flowContext.getButton().getValueTo());
            flowEntity.setFlowModifier(UserUtils.currentUser().getName());
            flowEntity.setFlowModifierCode(UserUtils.currentUser().getCode());
            flowEntity.setFlowModified(localDateTime);
            doFlowChange(flowContext, (T) flowEntity);

            //添加流程日志
            addFlowLog(flowContext, flowContext.getBusTable());
            afterHandleFlowChange(flowContext, (T) flowEntity);
            dataSourceTransactionManager.commit(transactionStatusMain);
        } catch (Exception e) {
            _rollbackBatch(transactionStatusFlowExecution, transactionStatusMain);
            throw e;
        }
    }

    /**
     * @Description： 流程前置处理，做一些校验相关的，请勿在此方法中进行业务处理等包含事务的处理
     * @flowContext  流程上下文，可以获取本次流程处理的相关参数
     * @newEntity    实体列表，可使用具体子类接收使用
     */
    protected void beforeHandleFlowChange(FlowContext flowContext, T newEntity) {
        log.debug("==============================={} {} {} 流程实际处理之前===============================",
                flowContext.getFlow().getName(), flowContext.getButton().getTitle(), flowContext.getBusId());
        String comment = flowContext.getComment();
        String attachmentUrl = flowContext.getAttachmentUrl();
        log.debug("审批意见：{}，附件地址：{}", StringUtils.defaultIfBlank(comment, "无"),
                StringUtils.defaultIfBlank(attachmentUrl, "无"));
    }

    private  static  String[] fieldIg = new String[]{"flowStatus","flowModified","flowModifier","flowModifierCode","printTime","snum","flagPrint","flowCloseRemark"};

    protected void doFlowChange(FlowContext flowContext, T entity) {
        if (flowContext.getButton().getFlagSave() != null && flowContext.getButton().getFlagSave() == true && flowContext.getEntityWeb() != null){
            BeanUtil.copyProperties( flowContext.getEntityWeb() ,entity, fieldIg);
        }
        this.updateById(entity);
    }

    /**@Description： 流程后置处理，注意目前只支持嵌套事务，此方法需要使用手动事务处理
     * @flowContext  流程上下文，可以获取本次流程处理的相关参数
     * @newEntity    实体列表，可使用具体子类接收使用
     */
    protected void afterHandleFlowChange(FlowContext flowContext, T newEntity) {
        log.debug("==============================={} {} {} 流程实际处理之后===============================",
                flowContext.getFlow().getName(), flowContext.getButton().getTitle(), flowContext.getBusId());
    }

    /**
     * 添加流程日志
     *
     * @param flowContext
     */
    private void addFlowLog(FlowContext flowContext, String busType) {
        if (flowLogService == null) {
            return;
        }

        Flow flow = flowContext.getFlow();
        FlowButton button = flowContext.getButton();

        Date now = new Date();

        // 当前节点日志
        FlowLog log = new FlowLog();
        log.setFlowId(flow.getId())
                .setFlowName(flow.getName())
                .setAction(button.getTitle())
                .setBusId(flowContext.getBusId())
                .setBusType(busType)
                .setBusName(UserUtils.currentUser().getName())
                .setFlagEnd(false)
                .setComment(flowContext.getComment())
                .setAttachmentUrl(flowContext.getAttachmentUrl())
                .setGmtEnd(now).setSignUrl(this.uploadSign(flowContext, busType));

        // 检测流程是否已存在流程日志
        int count = flowLogService.count(Wrappers.<FlowLog>query().eq("flow_id", flow.getId())
                .eq("bus_id", flowContext.getBusId()));

        boolean firstNode = false;
        if (count == 0) {
            FlowLog flowLog = new FlowLog();
            BeanUtils.copyProperties(log, flowLog);
            // 插入开始节点
            flowLog.setAction("开始").setFlagStart(true).setGmtStart(now).setGmtEnd(now).setAttachmentUrl(null).setComment(null).setFlagActive(false);
            flowLogService.save(flowLog);
            firstNode = true;
        }

        if (!firstNode) {
            // 修改上一个节点的结束时间
            flowLogService.update(Wrappers.<FlowLog>update().eq("flow_id", flow.getId())
                    .eq("bus_id", flowContext.getBusId())
                    .set("gmt_end", now)
                    .set("flag_active", false)
                    .orderByAsc("flag_start")
                    .orderByDesc("gmt_create").last("limit 1")
            );
        }

        // 插入当前节点
        boolean lastNode = flowContext.isEndNode();
        log.setAction(button.getTitle()).setFlagStart(false).setGmtStart(now).setGmtEnd(lastNode ? now : null).setFlagActive(true).setId(null);
        flowLogService.save(log);


        // 检测流程是否已完结
        if (lastNode) {
            log.setAction("结束").setFlagStart(false).setFlagEnd(true).setGmtStart(now).setGmtEnd(now).setId(null);
            flowLogService.save(log);
        }

    }

    /**
     * @Description：如果有base64数据，则上传图片
     */
    private String uploadSign(FlowContext flowContext, String busType) {
        if (StringUtils.isBlank(flowContext.getSignBase64())) return null;
        StringBuffer fileName = new StringBuffer();
        String base64Str = flowContext.getSignBase64();
        fileName.append(UUID.randomUUID().toString().replaceAll("-", ""));
        base64Str = base64Str.replace("data:image/png;base64,", "");
        fileName.append(".png");
        if (StringUtils.isBlank(busType)) busType = "unknown";

        String fileSavePath = "flowsign/" + busType + "/" + flowContext.getBusId() + "/";
        String fileAccessPath = fileSavePath;
        fileSavePath = resPath + fileSavePath;

        logger.debug("uploadSign.fileSavePath={}", fileSavePath);
        logger.debug("uploadSign.fileName={}", fileName.toString());
        File file = new File(fileSavePath, fileName.toString());
        byte[] fileBytes = Base64.getDecoder().decode(base64Str);
        try {
            FileUtils.mkdirs(fileSavePath);
            FileUtils.writeByteArrayToFile(file, fileBytes);
        } catch (IOException e) {
            logger.error("uploadSign失败", e);
            return null;
        }

        return "/res/" + fileAccessPath + fileName;

    }

    @Override
    public void saveBatchImport(Class<?> modelClass, Collection<?> entityList, int batchSize) {
        String sqlStatement = SqlHelper.table(modelClass).getSqlStatement(SqlMethod.INSERT_ONE.getMethod());

        try (SqlSession batchSqlSession = sqlSessionBatch()) {
            int i = 0;
            for (Object anEntityList : entityList) {
                batchSqlSession.insert(sqlStatement, anEntityList);
                if (i >= 1 && i % batchSize == 0) {
                    batchSqlSession.flushStatements();
                }
                i++;
            }
            batchSqlSession.flushStatements();
        }
    }

    /**
     * 去除前后空格
     *
     * @param entity
     * @return
     */
    private T trimEntity(T entity) {
        try {
            Class clazz = entity.getClass();
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                //主键和非String类型的过滤
                if (!field.getGenericType().toString().equals("class java.lang.String")) {
                    continue;
                }

                //              获取属性描述器
                PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(clazz, field.getName());
                if (pd==null) continue;
                Method getMethod = pd.getReadMethod();
                Method setMethod = pd.getWriteMethod();
                if (getMethod==null||setMethod==null) continue;
                String value = (String) getMethod.invoke(entity);
                if (StringUtils.isNotEmpty(value)) {
                    setMethod.invoke(entity, value.trim());
                }
                if (field.getName().equals("flowStatus")) {
                    log.debug("flowStatus");
                }

                if (value != null && StringUtils.isAllBlank(value)) {
                    EntityFieldAnnotation entityFieldAnnotation = field.getAnnotation(EntityFieldAnnotation.class);

                    if (entityFieldAnnotation != null &&  entityFieldAnnotation.not_null() && StringUtils.isBlank(entityFieldAnnotation.default_value())) {
                        BusinessException.throwValidFail("字段【%s】不允许为空，请检查或者联系管理员", field.getName());
                    }
                    if (entityFieldAnnotation != null &&  !entityFieldAnnotation.not_null() && StringUtils.isNotBlank(entityFieldAnnotation.default_value())) {
                        setMethod.invoke(entity, entityFieldAnnotation.default_value());
                    }
                }
            }

            if (entity instanceof  FlowEntity) {
                FlowEntity flowEntity = (FlowEntity) entity;
                if (StringUtils.isBlank(flowEntity.getFlowStatus())) {
                    BeanUtil.setFieldValue(entity,"flowStatus", GlobalConsts.FLOW_START_VALUE);
                }
            }
        } catch (ValidException ex) {
            logger.error("BaseServiceImpl.trimEntity"+ex.getMessage());
            BusinessException.throwValidFail(ex.getMessage());
        } catch (Exception ex) {
            logger.error("BaseServiceImpl.trimEntity"+ex.getMessage());
        }
        return entity;
    }


    /**
     * @throws BusinessException
     * @Description： 根据主键，更新某条记录的某些字段，主键名称固定为 id
     * @tableName 表名称
     * @pkValue 主键值
     * @setFields 需要更新的字段名称列表
     * @setFieldsValues 需要更新的字段值列表
     * @appendSq 值字段前后是否追加单引号，true:  pkValue，setFieldsValues 前后追加单引号
     */
    protected final void updateFields(String tableName, String pkValue, List<String> setFields, List<Object> setFieldsValues, boolean appendSq) {
        this.updateFields(
                tableName,
                "id",
                pkValue,
                setFields,
                setFieldsValues,
                appendSq
        );
    }

    /**
     * @throws BusinessException
     * @Description： 根据主键，更新某条记录的某些字段
     * @tableName 表名称
     * @pkName 主键名称
     * @pkValue 主键值
     * @setFields 需要更新的字段名称列表
     * @setFieldsValues 需要更新的字段值列表
     * @appendSq 值字段前后是否追加单引号，true:  pkValue，setFieldsValues 前后追加单引号
     */
    protected final void updateFields(String tableName, String pkName, String pkValue, List<String> setFields, List<Object> setFieldsValues, boolean appendSq) {
        if (StringUtils.isBlank(tableName)) throw new BusinessException("缺少参数【tableName】");
        if (StringUtils.isBlank(pkName)) throw new BusinessException("缺少参数【pkName】");
        if (StringUtils.isBlank(pkValue)) throw new BusinessException("缺少参数【pkValue】");
        if (setFields == null || setFields.size() < 1) throw new BusinessException("缺少参数【setFields】");
        if (setFieldsValues == null || setFieldsValues.size() < 1) throw new BusinessException("缺少参数【setFieldsValues】");
        if (setFields.size() != setFieldsValues.size()) throw new BusinessException("setFields与setFieldsValues长度不匹配");

        synchronized (pkValue.intern()) {
            List<String> setConditions = new ArrayList<>();
            for (int i = 0; i < setFields.size(); i++) {
                if (appendSq) {
                    setConditions.add(" " + setFields.get(i) + " = '" + setFieldsValues.get(i) + "'");
                } else {
                    setConditions.add(" " + setFields.get(i) + " = " + setFieldsValues.get(i));
                }
            }
            String updateSql = " update " + tableName + " set " + StringUtils.join(setConditions, ",") + " where ";
            if (appendSq) {
                updateSql += pkName + " = '" + pkValue + "'";
            } else {
                updateSql += pkName + " = " + pkValue;
            }
            this.jdbcTemplate.update(updateSql);
        }
    }


    @Override
    /** 【事务调用者处理】
     * @Description： 批量作废，只修改主单据状态，增加流程日志
     * @param entityList 继承FlowEntity的子类列表   【必须】
     * @param closeRemark 作废原因，不传则为 无原因作废
     * @param flowStatusFilterList 哪些状态可以作废
     */
    @Transactional(rollbackFor = Exception.class)
    public void closeBatch(List<?> entityList, String closeRemark, String... flowStatusFilterList) {
        closeRemark = UtilCommon.getStr(closeRemark, "无原因作废");
        List<FlowEntity> entityList2 = (List<FlowEntity>) entityList;
        List<String> idList = entityList2.stream().map(flowEntity -> flowEntity.getId()).collect(Collectors.toList());
        UpdateWrapper updateWrapper = Wrappers.<T>update();
        updateWrapper.in("id", idList);
        updateWrapper.set("flow_status", GlobalConsts.FLOW_CLOSE_VALUE);
        updateWrapper.set("flow_close_remark", closeRemark);
        updateWrapper.ne("ifnull(flow_status,'')", "close");
        if (flowStatusFilterList != null && flowStatusFilterList.length > 0)
            updateWrapper.in("ifnull(flow_status,'')", flowStatusFilterList);
        this.update(updateWrapper);

        this.addFlowLogBatch(entityList, closeRemark, null);
    }


    /**
     * 【事务调用者处理】
     *
     * @param entityList           继承FlowEntity的子类列表   【必须】
     * @param closeRemark          作废原因，不传则为 无原因作废
     * @param flowStatusFilterList 哪些状态可以作废
     * @Description： 批量作废，只修改主单据状态，增加流程日志
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addFlowLogBatch(List<?> entityList, String remark, Flow flow) {
        List<FlowEntity> entityList2 = (List<FlowEntity>) entityList;
        List<String> idList = entityList2.stream().map(flowEntity -> flowEntity.getId()).collect(Collectors.toList());
        //流程日志
        TableInfo tableInfo = SqlHelper.table(entityList.get(0).getClass());
        Date nowDate = DateUtils.now();
        List<FlowLog> flowLogList = new ArrayList<FlowLog>();
        if (flow == null) flow = new Flow();
        Flow finalFlow = flow;
        idList.forEach(id -> {
            FlowLog log = new FlowLog();
            log.setFlowId(finalFlow.getId())
                    .setFlowName(finalFlow.getName())
                    .setAction("作废")
                    .setBusId(id)
                    .setBusType(tableInfo.getTableName())
                    .setBusName(UserUtils.currentUser() != null ? UserUtils.currentUser().getName() : "无")
                    .setFlagEnd(true)
                    .setComment(remark)
                    .setAttachmentUrl(null)
                    .setGmtEnd(nowDate);
            flowLogList.add(log);
        });
        flowLogService.saveBatch(flowLogList);
    }

    /**
     * wm_order_barcode 中是否有该sn
     *
     * @param sn sn
     * @throws BusinessException 未找到
     */
    public void haveSn(String sn) {
        Stock stock = stockService.getOne(Wrappers.<Stock>query().eq("sn", sn).or().eq("IFNULL(rfid,'')", sn), false);
        if (stock != null) {
            return;
        }
        StockZero stockZero = stockZeroService.getOne(Wrappers.<StockZero>query().eq("sn", sn).or().eq("IFNULL(rfid,'')", sn), false);
        if (stockZero != null) {
            return;
        }
        OrderBarcode orderBarcode = orderBarcodeService.getOne(Wrappers.<OrderBarcode>query().eq("sn", sn).or().eq("IFNULL(rfid,'')", sn), false);
        if (orderBarcode != null) {
            return;
        }
        StockHistory stockHistory = stockHistoryService.getOne(Wrappers.<StockHistory>query().eq("sn", sn).or().eq("IFNULL(rfid,'')", sn), false);
        if (stockHistory != null) {
            return;
        }
        throw new BusinessException("库存、0库存、系统编码记录、历史库存均未找到该标签【%s】，请检查", sn);
    }

    //顶部合计
    @Override
    public Map<String, Double> sum(QueryContext context, String sumFields) {
        String sqlStatement = SqlHelper.table(currentModelClass()).getSqlStatement("sum");
        if (sqlSessionFactory.getConfiguration().hasStatement(sqlStatement)) {
            // 说明在mapper.xml中实现了自定义list方法
            return this.baseMapper.sum(context.getWrapper(), context.getParams());
        } else {
            List list = this.list(context.getWrapper());
            if (list == null || list.size() < 1) return null;

            String[] sumFieldArr = sumFields.split(",");

            //暂时使用以下遍历方法
            Map<String, Double> mapNum = new HashMap<String, Double>();
            for (Object od : list) {
                JSONObject jsonOb = JSON.parseObject(JSON.toJSONString(od));
                for (String field : sumFieldArr) {
                    if (!mapNum.containsKey(field)) mapNum.put(field, 0d);
                    jsonOb.keySet().stream().filter(key -> key.toLowerCase().equalsIgnoreCase(field)).findFirst().ifPresent( keyFind -> {
                        mapNum.put(field, UtilNum.sum(mapNum.get(field), jsonOb.getString(keyFind)));
                    });
                }
            }
            return mapNum;
        }
    }


    /**
    * @Description： 批量流程操作
    */
    @Override
    public void handleFlowChangeBatch() {
        FlowContext flowContext = FlowContextHolder.currentContext();
        //处理前事务状态
        TransactionStatus transactionStatusBefore = null;

        //处理事务状态
        TransactionStatus transactionStatusMain = null;
        TransactionStatus transactionStatusAfter = null;
        TransactionStatus transactionStatusLog = null;
        try {
            Collection<T> entityListCollection =  this.listByIds(flowContext.getBusIdList());
            List<FlowEntity> flowEntityList = (List<FlowEntity>) entityListCollection;

            //1、 获取或者新增流程实例
            transactionStatusBefore = dataSourceTransactionManager.getTransaction(transactionDefinition);
            List<FlowExecution> flowExecutionList = flowExecutionService.getByBusIdsWithCreate(flowEntityList,flowContext);
            dataSourceTransactionManager.commit(transactionStatusBefore);

            //1.2 通用校验 校验流程状态、流程实例状态等
            this.beforeCheckHandleFlowChangeBatch(flowEntityList, flowExecutionList);

            //执行可覆盖的前置方法，可继承的，一般只做判断使用
            beforeHandleFlowChangeBatch(flowContext, (List<T>) entityListCollection, flowContext.getBusIdList());

            //2.1、 更新流程状态、流程实例状态
            transactionStatusMain = dataSourceTransactionManager.getTransaction(transactionDefinition);
            //更新流程状态
            LocalDateTime localDateTime = LocalDateTime.now();
            Date now = DateUtils.localDateTime2Date(localDateTime);
            flowEntityList.stream().forEach(flowEntity -> {
                flowEntity.setFlowStatus(flowContext.getButton().getValueTo());
                flowEntity.setFlowModifier(UserUtils.currentUser().getName());
                flowEntity.setFlowModifierCode(UserUtils.currentUser().getCode());
                flowEntity.setFlowModified(localDateTime);
            });
            this.updateBatchById((Collection<T>) flowEntityList);

            //更新流程示例状态
            flowExecutionList.stream().forEach(flowExecution -> {
                flowExecution.setFlowStatus(flowContext.getButton().getValueTo()).setFlowStatusText(flowContext.getFlowStatusTextTo());
                flowExecution.setBusUser(UserUtils.currentUser().getName()).setBusDate(now);
            });
            flowExecutionService.updateBatchById(flowExecutionList);
            dataSourceTransactionManager.commit(transactionStatusMain);

            //执行可覆盖后置方法，一般做业务处理
            afterHandleFlowChangeBatch(flowContext, (List<T>) entityListCollection, flowContext.getBusIdList());


            //9.1、回滚失败的流程状态、流程实例状态
            if (flowContext.getBusIdErrorList() != null && flowContext.getBusIdErrorList().size() > 0){
                transactionStatusAfter = dataSourceTransactionManager.getTransaction(transactionDefinition);
                List<FlowEntity> flowEntityListError = new ArrayList<FlowEntity>();
                flowEntityList.stream().filter(tmp -> flowContext.getBusIdErrorList().contains(tmp.getId())).forEach(flowEntity -> {
                    flowEntity.setFlowStatus(flowContext.getButton().getValueIf());
                    flowEntity.setFlowModifier(UserUtils.currentUser().getName());
                    flowEntity.setFlowModifierCode(UserUtils.currentUser().getCode());
                    flowEntity.setFlowModified(localDateTime);
                    flowEntityListError.add(flowEntity);
                });
                this.updateBatchById((Collection<T>) flowEntityListError);

                List<FlowExecution> flowExecutionListError = new ArrayList<FlowExecution>();
                flowExecutionList.stream().filter(tmp -> flowContext.getBusIdErrorList().contains(tmp.getId())).forEach(flowExecution -> {
                    flowExecution.setFlowStatus(flowContext.getButton().getValueIf()).setFlowStatusText(flowContext.getFlowStatusTextFrom());
                    flowExecution.setBusUser(UserUtils.currentUser().getName()).setBusDate(now);
                    flowExecutionListError.add(flowExecution);
                });

                flowExecutionService.updateBatchById(flowExecutionListError);
                dataSourceTransactionManager.commit(transactionStatusAfter);
            }


            //9.2、保存日志
            transactionStatusLog = dataSourceTransactionManager.getTransaction(transactionDefinition);
            addFlowLogBatch(flowContext.getBusIdList());
            dataSourceTransactionManager.commit(transactionStatusLog);

            String msgEnd ="";
            if (flowContext.getBusErrorMsgList() != null && flowContext.getBusErrorMsgList().size() > 0) {
                int index = 0;
                for(String msgTmp : flowContext.getBusErrorMsgList()) {
                    msgEnd+="<br/>单据："+flowContext.getBusKeyErrorList().get(index)+"  " +msgTmp;
                    index++;
                }
            }
            flowContext.setHandledMsg(
                    String.format(
                            "批量%s操作成功,数量：%s，成功：%s，失败：%s"+msgEnd
                            ,flowContext.getButton().getTitle()
                            ,flowContext.getBusIdList().size()
                            ,flowContext.getBusIdList().size() - (flowContext.getBusIdErrorList() != null ? flowContext.getBusIdErrorList().size() : 0)
                            ,flowContext.getBusIdErrorList() != null ? flowContext.getBusIdErrorList().size() : 0
                    )
            );
        } catch (Exception e) {
            log.error("handleFlowChangeBatch.error",e);
            _rollbackBatch(transactionStatusBefore,transactionStatusMain,transactionStatusAfter,transactionStatusLog);
            flowContext.setHandledMsg(
                    String.format(
                            "批量%s操作失败,数量：%s，成功：%s，失败：%s<BR>"+e.getMessage()
                            ,flowContext.getButton().getTitle()
                            ,flowContext.getBusIdList().size()
                            ,0
                            ,flowContext.getBusIdList().size()
                    )
            );
        }
    }

    /**
    * @Description： 批量回滚
    */
    private void _rollbackBatch(TransactionStatus... transactionStatuseList) {
        for(TransactionStatus transactionStatus : transactionStatuseList) {
            if (transactionStatus != null) {
                //手动事务回滚
                try {
                    dataSourceTransactionManager.rollback(transactionStatus);
                }catch (Exception e1) {
                    log.error("_rollbackBatch.rollbackerror",e1);
                }
            }
        }
    }

    /**
    * @Description： 批量流程操作前的一些校验
     * 1、校验数据长度
     * 2、校验数据流程状态
    */
    private void beforeCheckHandleFlowChangeBatch(List<FlowEntity> entityList,List<FlowExecution> flowExecutionList) {
        log.debug("=============================== beforeCheckHandleFlowChangeBatch===============================");
        FlowContext flowContext = FlowContextHolder.currentContext();
        //校验所操作的数据是否有不符合的
        Collection<FlowEntity> entityListError = entityList.stream().filter(ob -> !ob.getFlowStatus().equals(flowContext.getButton().getValueIf())).collect(Collectors.toList());
        if (entityListError != null && entityListError.size() > 0){
            BusinessException.throwValidFail("所选单据%s流程状态，不可进行当前操作，请检查", entityListError.size());
        }

        //校验所操作的数据是否有不符合的
        Collection<FlowExecution> flowExecutionListError = flowExecutionList.stream().filter(ob -> !ob.getFlowStatus().equals(flowContext.getButton().getValueIf())).collect(Collectors.toList());
        if (flowExecutionListError != null && flowExecutionListError.size() > 0){
            if (flowContext.getFlow().getFlagCheckExecution() != null && flowContext.getFlow().getFlagCheckExecution()) {
                BusinessException.throwValidFail("所选单据%s流程实例状态，不可进行当前操作，请检查", flowExecutionListError.size());
            }
            log.error(
                    "流程实例与数据状态出现了不匹配，流程编码={}，流程实例主键列表={}",
                    flowContext.getFlow().getCode(),
                    StringUtils.joinWithNull(
                            flowExecutionListError.stream().map(FlowExecution::getId).collect(Collectors.toList()),
                            ","
                    )
            );
        }
    }
    /**
    * @Description： 批量流程操作前的一些校验
     * 1、校验数据长度
     * 2、校验数据流程状态
    */
    private void addFlowLogBatch(List<String> busIdList) {
        FlowContext flowContext = FlowContextHolder.currentContext();

        //都失败了
        if (flowContext.getBusIdErrorList() != null && flowContext.getBusIdErrorList().size() == busIdList.size()) {
            return;
        }
        Date now = new Date();
        List<String> busIdListSuccess = busIdList;
        if (flowContext.getBusIdErrorList() != null && flowContext.getBusIdErrorList().size() > 0) {
            busIdListSuccess = busIdListSuccess.stream().filter(tmp -> !flowContext.getBusIdErrorList().contains(tmp)).collect(Collectors.toList());
        }
        // 当前节点日志
        List<FlowLog> flowLogList = new ArrayList<FlowLog>();
        for(String busId : busIdListSuccess) {
            FlowLog log = new FlowLog();
            log.setFlowId(flowContext.getFlow().getId()).setFlagActive(true).setFlagStart(false).setFlagEnd(false)
                    .setFlowName(flowContext.getFlow().getName())
                    .setAction(flowContext.getButton().getTitle())
                    .setBusId(busId)
                    .setBusType(flowContext.getBusTable())
                    .setBusName(UserUtils.currentUser().getName())
                    .setComment(flowContext.getComment())
                    .setAttachmentUrl(flowContext.getAttachmentUrl())
                    .setGmtStart(now).setSignUrl(this.uploadSign(flowContext, "表名称"));

            if (flowContext.getButton().getFlagStart()){ // 开始节点
                log.setAction("开始").setFlagStart(true);
            }else if (flowContext.getButton().getFlagEnd()){ //结束节点
                log.setAction("结束").setFlagEnd(true).setGmtEnd(now).setFlagActive(false);
            }
            flowLogList.add(log);
        }

        //修改当前激活的节点状态为未激活
        flowLogService.update(Wrappers.<FlowLog>update().eq("flow_id", flowContext.getFlow().getId())
                .in("bus_id", busIdListSuccess).eq("flag_active", true)
                .set("gmt_end", now).set("flag_active", false)
        );

        flowLogService.saveBatch(flowLogList);

    }

    /**
     * @Description： 流程批量前置处理，做一些校验相关的，请勿在此方法中进行业务处理等包含事务的处理
     * <p>
     *
     * </p>
     * @flowContext  流程上下文，可以获取本次流程处理的相关参数
     * @entityList    实体列表，可使用具体子类接收使用
     * @busIdList     实体id列表
     */
    protected void beforeHandleFlowChangeBatch(FlowContext flowContext, List<T> entityList,List<String> busIdList) {
    }

    /**@Description： 流程批量后置处理，注意目前只支持嵌套事务，此方法需要使用手动事务处理，具体参考   TransferServiceImpl.afterHandleFlowChangeBatch
     * @flowContext  流程上下文，可以获取本次流程处理的相关参数
     * @entityList    实体列表，可使用具体子类接收使用
     * @busIdList     实体id列表
     */
    protected void afterHandleFlowChangeBatch(FlowContext flowContext, List<T> entityList,List<String> busIdList) {
    }



    /**@Description： 导入模板字段标题
     */
    protected List<ExcelImportField> excelGetFieldHead() {
        List<ExcelImportField> exportHeadList = new ArrayList<ExcelImportField>();
        ExcelImportField headDTO;

        headDTO = new ExcelImportField();
        headDTO.setName("name1").setText("字段1").setCellIndex(0);
        exportHeadList.add(headDTO);

        headDTO = new ExcelImportField();
        headDTO.setName("name2").setText("字段2").setCellIndex(1);
        exportHeadList.add(headDTO);
        return  exportHeadList;
    }

    //下载导入模板
    @Override
    public void excelDownloadTemp(HttpServletResponse response, Map<String, String> reqData,List<ExcelImportField> exportHeadListParam) {
        response.setHeader("content-type", "application/octet-stream");
        response.setContentType("application/octet-stream");
        String sheetName = reqData.get("sheetName");
        String fileName = StrUtil.format("{}导入_{}.xls",sheetName,DateUtils.format(DateUtils.now(),DateUtils.YYYYMMDDHHMMSS_PATTER));
        try {
            response.setHeader("Content-Disposition", "attachment;filename=" + java.net.URLEncoder.encode(fileName, "UTF-8"));
        } catch (UnsupportedEncodingException e2) {
            e2.printStackTrace();
        }

        HSSFWorkbook workbook = new HSSFWorkbook();
        HSSFSheet sheet = workbook.createSheet(sheetName);
//        sheet.protectSheet("123456");
        int rowNextNum = 0;

        List<ExcelImportField> exportHeadList = exportHeadListParam;
        if (exportHeadList == null) {
            exportHeadList = excelGetFieldHead();
        }
        //表头列-一级
        ExcelImportField exportHeadDTO = null;
        HSSFRow row;
        HSSFCell cell;
        HSSFCellStyle cellStyle;
        HSSFFont font;
        HSSFRichTextString richTextString = null;
        HSSFCellStyle cellStyleTitleDefault;
        cellStyleTitleDefault = workbook.createCellStyle();
        //水平居中
        cellStyleTitleDefault.setAlignment(HSSFCellStyle.ALIGN_CENTER);

        font = workbook.createFont();
        //字体大小
        font.setFontHeightInPoints((short) 14);
        font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);   //字体加粗
        cellStyleTitleDefault.setFont(font);
//        cellStyleTitleDefault.setLocked(true);

        HSSFCellStyle cellStyleTitleReq;
        HSSFFont fontReq;
        fontReq = workbook.createFont();
        //字体大小
        fontReq.setFontHeightInPoints((short) 14);
        fontReq.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);   //字体加粗
        fontReq.setColor(Font.COLOR_RED);
        cellStyleTitleReq = workbook.createCellStyle();
        //水平居中
        cellStyleTitleReq.setAlignment(HSSFCellStyle.ALIGN_CENTER);
        cellStyleTitleReq.setFont(fontReq);
//        cellStyleTitleReq.setLocked(true);


        //单元格锁定的样式
        HSSFCellStyle unlockstyle = workbook.createCellStyle();
        unlockstyle.setLocked(false);

        //单元格锁定的样式
//        HSSFCellStyle unlockstyle = workbook.createCellStyle();
//        unlockstyle.setLocked(false);
//        unlockstyle.setBorderBottom(CellStyle.BORDER_THIN); //底部边框
//        unlockstyle.setBottomBorderColor(IndexedColors.GREEN.getIndex());//底部边框颜色
//
//        unlockstyle.setBorderTop(CellStyle.BORDER_THIN); //底部边框
//        unlockstyle.setTopBorderColor(IndexedColors.GREEN.getIndex());//底部边框颜色
//
//        unlockstyle.setBorderLeft(CellStyle.BORDER_THIN); //底部边框
//        unlockstyle.setLeftBorderColor(IndexedColors.GREEN.getIndex());//底部边框颜色
//
//        unlockstyle.setBorderRight(CellStyle.BORDER_THIN); //底部边框
//        unlockstyle.setRightBorderColor(IndexedColors.GREEN.getIndex());//底部边框颜色

        row = sheet.createRow(rowNextNum);
        int cellIndex = 0;
        for (int i = 0; i < exportHeadList.size(); i++) {
            exportHeadDTO = exportHeadList.get(i);
            cell = row.createCell(cellIndex);
            cellIndex++;
            richTextString = new HSSFRichTextString(exportHeadDTO.getText());
            cell.setCellValue(richTextString);

            if (exportHeadDTO.isRequired()) {
                cell.setCellStyle(cellStyleTitleReq);
            } else{
                cell.setCellStyle(cellStyleTitleDefault);
            }

            sheet.setDefaultColumnStyle(i, unlockstyle);
            //cell宽度
            sheet.setColumnWidth(i, (int) (UtilCommon.getStr(exportHeadDTO.getText(), "").getBytes().length * 1.2d * 256 > 12 * 256 ? UtilCommon.getStr(exportHeadDTO.getText(), "").getBytes().length * 1.2d * 256 : 12 * 256));
        }

        OutputStream os1 = null;
        FileUtils.mkdirs(resPath + "download");
        String tmpFilePath = resPath + "download/"+fileName;
        java.io.File tmpFile = new java.io.File(tmpFilePath);
        byte[] buff = new byte[1024];
        BufferedInputStream bis = null;
        OutputStream os2 = null;

        try {
            os1 = new FileOutputStream(tmpFile);
            workbook.write(os1);

            os2 = response.getOutputStream();
            bis = new BufferedInputStream(new FileInputStream(tmpFile));
            int i = bis.read(buff);
            while (i != -1) {
                os2.write(buff, 0, buff.length);
                os2.flush();
                i = bis.read(buff);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public List<T> excelGetDataList(MultipartFile file, Map<String, String> reqData,List<ExcelImportField> exportHeadList) {
        FileUtils.mkdirs(resPath + "import");
        String sheetName = reqData.get("sheetName");
        String fileName = StrUtil.format("{}导入临时文件_{}.xls",sheetName,DateUtils.format(DateUtils.now(),DateUtils.YYYYMMDDHHMMSS_PATTER));
        String tmpFilePath = resPath + "import/"+fileName;
        java.io.File fileTmp = new java.io.File(tmpFilePath);
        try {
            BusinessException.throwValidFailIf(!"XLS".equals(FileUtils.getFileSuffixWithoutDot(file).toUpperCase()), "请上传xls后缀的表格");
            if (!fileTmp.exists()) {
                fileTmp.createNewFile();
            }
            // 底层也是通过io流写入文件file
            FileCopyUtils.copy(file.getBytes(), fileTmp);
        } catch (ValidException e) {
            throw new ValidException(e.getMessage());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        ArrayList<String[]> arrLst = null;//获取第一个sheet页
        try {
            arrLst = UtilExcelPoi.getListFromXlsFile(fileTmp, 0);//获取第一个sheet页
        }catch (ValidException e) {
            throw e;
        }

        List<String> arrHeadLst = Arrays.asList(arrLst.get(0));

        List<String> arrHeadLstCn = UtilCommon.getListOnlyCn(arrHeadLst);
        int findIndex = 0;
        for(ExcelImportField fieldHead  : exportHeadList) {
            findIndex = UtilCommon.getListIndex(arrHeadLstCn,fieldHead.getText());
            BusinessException.throwValidFailIf(
                    findIndex != fieldHead.getCellIndex(),
                    "列[%s]位置错误，请查看excel模板，需要位置%s，实际位置%s",
                    fieldHead.getText(),
                    fieldHead.getCellIndex() + 1,
                    findIndex + 1
            );
        }

        String[] arrRow = null;
        T itemImport;
        List<T> itemImportList = new ArrayList<>();
        String value,valueTmp;
        Map<String,List<String>> mapValueList = new HashMap<>();
        boolean rowHasData = false;
        for (int nRow = 1; nRow < arrLst.size(); nRow++) {
            arrRow = arrLst.get(nRow);//Excel导数据的每行数据列
            try {
                itemImport = currentModelClass().newInstance();
            } catch (InstantiationException e) {
                throw new RuntimeException(e);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }

            // 该行都为空数据，则不处理
            rowHasData = false;
            for (int i = 0; i < arrRow.length; i++) {
                valueTmp = arrRow[i];
                if (!UtilCommon.strIsNull(valueTmp)) {
                    rowHasData = true;
                }
            }
            if (!rowHasData) {
                continue;
            }

            for(ExcelImportField head : exportHeadList) {
                value = StringUtils.trim(arrRow[head.getCellIndex()]);
                if (head.isRequired() && StringUtils.isBlank(value)) {
                    BusinessException.throwValidFail("第[%s]行，[%s]必填，请检查",nRow +1 +"",head.getText());
                }
                if (
                        head.isUnq()
                                && StringUtils.isNotBlank(value)
                                && mapValueList.containsKey(head.getName())
                                && mapValueList.get(head.getName()).contains(value)
                ) {
                    BusinessException.throwValidFail("第[%s]行，[%s]重复出现，请检查",nRow +1 +"",head.getText());
                }

                if (!mapValueList.containsKey(head.getName())) {
                    mapValueList.put(head.getName(), new ArrayList<>());
                }
                mapValueList.get(head.getName()).add(value);

                if (StringUtils.isNotBlank(value)) {
                    if (head.isNumber() && !NumberUtils.isNumber(value)) {
                        BusinessException.throwValidFail("第[%s]行，[%s]格式应为数字，请检查",nRow +1 +"",head.getText());
                    }
                    if (head.isDate() && !DateUtils.isValidDate(value,DateUtils.DATE_PATTERN)) {
                        // 自定义日期格式无法识别，此处增加个转换
                        try {
                            value = DateUtils.excelDaysToDate(Integer.valueOf(UtilNum.trimZero(value)));
                        }catch (Exception e2) {
                            Date dateNew = DateUtils.parse(value);
                            if (dateNew == null) {
                                BusinessException.throwValidFail("第[%s]行，[%s]格式应为日期，请检查",nRow +1 +"",head.getText());
                            } else{
                                value =DateUtils.format(dateNew);
                            }
                        }

                        if (!DateUtils.isValidDate(value,DateUtils.DATE_PATTERN)) {
                            BusinessException.throwValidFail("第[%s]行，[%s]格式应为日期，请检查",nRow +1 +"",head.getText());
                        }
                    }
                    if (head.getMaxLength() != null && UtilCommon.length(value) > head.getMaxLength()) {
                        BusinessException.throwValidFail("第[%s]行，[%s]长度已超过最大值[%s]，请检查",nRow +1 +"",head.getText(), head.getMaxLength());
                    }
                    if (head.getRequiredLength() != null && head.getRequiredLength() > 0 && UtilCommon.length(value) != head.getRequiredLength()) {
                        BusinessException.throwValidFail("第[%s]行，[%s]长度应该为[%s]，请检查",nRow +1 +"",head.getText(), head.getRequiredLength());
                    }
                    BeanUtil.setFieldValue(itemImport,head.getName(),value);
                }
            }
            itemImportList.add(itemImport);
        }
        if (itemImportList.isEmpty()) {
            BusinessException.throwValidFail("没有可导入的数据");
        }
        return  itemImportList;
    }

    // 导入数据 - 获取excel中的数据
    @Override
    public String excelImport(MultipartFile file, Map<String, String> reqData) {
        List<T> itemImportList = excelGetDataList(file,reqData,excelGetFieldHead());
        return  excelImportToDb(reqData, itemImportList);
    }

    // 导入数据 到数据库中
    protected String excelImportToDb(Map<String, String> reqData, List<T> itemImportList) {
        return "未实现";
    }


}
